package uy.org.softtech.cssuite.web.controladores;

import static org.springframework.util.Assert.notNull;

import java.util.Locale;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import uy.org.softtech.cssuite.core.Comando;
import uy.org.softtech.cssuite.core.Controlador;
import uy.org.softtech.cssuite.excepciones.TokenExpiradoException;
import uy.org.softtech.cssuite.servicios.ServiciosUsuarios;
import uy.org.softtech.cssuite.web.modelo.ModeloCredenciales;
import uy.org.softtech.cssuite.web.modelo.ModeloCredenciales.Paso;

/**
 * Controlador encargado de manejar los cambios de credenciales de las cuentas de usuario.
 * 
 * @author fabian.lobo
 */
@Configuration
@Controller("controlador-credenciales")
@ManagedResource(
	objectName = "cssuite:type=Controlador,name=ControladorCredenciales", 
	description = "Controlador encargado de manejar los cambios de credenciales de las cuentas de usuario.",
	log = true, logFile = "jmx.log", currencyTimeLimit = 15,
	persistPolicy = "OnUpdate", persistPeriod = 200
)
public class ControladorCredenciales extends Controlador {
	
	private Logger log = Logger.getLogger(getClass());
	
	// Propiedaes
	@Value(value = "#{'${captcha.clave.publica}'}")
	private String clavePublicaCaptcha;
		
	// Comandos
	@Resource(name = "comando-credenciales")
	private Comando comandoCredenciales;
	
	// Servicios
	@Resource(name = "servicios-usuarios" )
	private ServiciosUsuarios serviciosUsuarios;
	
	/**
	 * Método para verificar que todos los parámetros han sido seteados correctamente.
	 * 
	 * @author fabian.lobo
	 */
	@PostConstruct
	public void inicializacion(){
		super.inicializacion();
		
		notNull(clavePublicaCaptcha(), "La propiedad [clavePublicaCaptcha] de [" + getClass().getName() + "] no ha sido establecida.");
		notNull(comandoCredenciales(), "La propiedad [comandoCredenciales] de [" + getClass().getName() + "] no ha sido establecida.");
		notNull(serviciosUsuarios(), "La propiedad [serviciosUsuarios] de [" + getClass().getName() + "] no ha sido establecida.");
		
		log.info("Controlador encargado de manejar los cambios de credenciales de las cuentas de usuario ha sido inicializado.");
	}
	
	/* 
	 * ---------------------------------------
	 * -- SOLICITAR CAMBIO DE CREDENCIALES ---
	 * ---------------------------------------
	 */
	
	/**
	 * Retorna la página para solicitar el cambio de credenciales.
	 * 
	 * @param modelo - Entidad encargada de contener los datos ingresados y mostrados en la página
	 * @author fabian.lobo
	 */
	@RequestMapping(method = RequestMethod.GET, value = "credenciales.do")
	@Secured(value = {"IS_AUTHENTICATED_ANONYMOUSLY"})
	public String solicitudCambioCredenciales(@ModelAttribute(value = "modeloCredenciales") ModeloCredenciales modelo){
		modelo.setAccion(Accion.CREDENCIALES);
		modelo.setPaso(Paso.SOLICITAR);		
		
		return "credenciales/solicitar";
	}
	
	/**
	 * Procesa el link enviado por correo electrónico para continuar el proceso de cambio de credenciales.
	 * 
	 * @author fabian.lobo
	 */
	@RequestMapping(method = RequestMethod.GET, value = "credenciales.do", params = {"accion", "paso", "token"})
	@Secured(value = {"IS_AUTHENTICATED_ANONYMOUSLY"})
	public ModelAndView respuestaSolicitudCambioCredenciales(@ModelAttribute(value = "modeloCredenciales") ModeloCredenciales modelo) {
		ModelAndView respuesta = new ModelAndView("credenciales/cambiar");
		respuesta.addObject("clavePublicaCaptcha", clavePublicaCaptcha());
		
		try {
			serviciosUsuarios().buscarUsuarioPorToken(modelo.getToken());			
		} catch (TokenExpiradoException e) {
			log.error(e);
			return new ModelAndView("redirect:expirado.do");
		}
		return respuesta;
	}
	
	/**
	 * Procesa la solicitud de cambio de credenciales.
	 * 
	 * @author fabian.lobo
	 */
	@RequestMapping(method = RequestMethod.POST, value = "credenciales.do")
	@Secured(value = {"IS_AUTHENTICATED_ANONYMOUSLY"})
	public ModelAndView procesaSolicitudCambioCredenciales(Locale locale, @ModelAttribute(value = "modeloCredenciales") ModeloCredenciales modelo, BindingResult errores) {
		ModelAndView respuesta = new ModelAndView();
		Paso paso = modelo.getPaso();
		
		switch(paso) {
		case SOLICITAR:
			respuesta.setViewName("credenciales/solicitar");
			break;
		case RESTABLECER:
			respuesta.setViewName("credenciales/cambiar");
			respuesta.addObject("clavePublicaCaptcha", clavePublicaCaptcha());
			
			//Verificación de validez del token
			try {
				modelo.setUsuario(serviciosUsuarios().buscarUsuarioPorToken(modelo.getToken()));
			} catch (TokenExpiradoException e) {
				log.error(e);
				return new ModelAndView("redirect:expirado.do");
			}
			break;
		}
			
		modelo.setLocale(locale);
		try {
			boolean ejecutado = comandoCredenciales().ejecutar(modelo, errores);
			if(ejecutado) {
				String mensaje = "";
				switch(paso) {
				case SOLICITAR:
					mensaje = fuenteMensajes().getMessage("mensaje.notificacionEnviada", null, "En breve recibirá un correo electrónico con las instrucciones.", locale);
					break;
				case RESTABLECER:
					mensaje = fuenteMensajes().getMessage("mensaje.credencialesModificadas", null, "Ya puede ingresar al sistema con sus nuevas credenciales.", locale);
					break;
				}
				mostrarMensajeInformacion(respuesta, mensaje, true, "entrar.do");
				return respuesta;
			}
		} catch (Exception e) {
			log.error(e);
			registrarErrorGeneral(errores, locale);
		}
		return respuesta;
	}
	
	/*
	 *  GETTERS
	 */
	public String clavePublicaCaptcha() {
		return clavePublicaCaptcha;
	}
	public Comando comandoCredenciales() {
		return comandoCredenciales;
	}
	public ServiciosUsuarios serviciosUsuarios() {
		return serviciosUsuarios;
	}
	
}
